﻿using AForge.Video;
using Scrat.Core.Common;
using Scrat.Core.Pojo;
using Scrat.Core.Screenshot;
using Scrat.Core.Server;
using Scrat.Student.Forms;
using Sunny.UI;
using System;
using System.Drawing;
using System.IO;
using System.Management.Instrumentation;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Windows.Forms;
using static Scrat.Core.Screenshot.Resolution;

namespace Scrat.Student
{
    public partial class MainForm : UIForm
    {
        // 聊天页面的显示状态
        private bool _chatPageVisibleStatus = false;
        // 接收教师屏幕广播
        private bool _screenRecServerStatus = false;

        private UDPMessageServer _udpServer;

        private StreamingServer _streamingServer;

        private static string _localServerIPAddr = "127.0.0.1";
        private static int _localMessagePort = 9988;
        private static int _localScreenSharingPort = 2333;
        
        private static string _remoteServerIPAddr = "127.0.0.1";
        private static int _remoteMessagePort = 8899;
        private static int _remoteScreenSharingPort = 2333;

        public MainForm()
        {
            InitializeComponent();
            // 程序加载,默认聊天框隐藏
            chatPageContainer.Visible = false;
        }

        /// <summary>
        /// 顶部聊天toggle按钮单击事件,控制聊天页面的显示与隐藏
        /// </summary>
        /// <param name="sender">触发者</param>
        /// <param name="e"></param>
        private void btnToggleChatPage_Click(object sender, EventArgs e)
        {
            // Size的 +-300 为右侧聊天页面的宽度
            if (!_chatPageVisibleStatus)
            {
                _chatPageVisibleStatus = true;
                this.Size = new Size(this.Width + 300, this.Height);
                chatPageContainer.Visible = _chatPageVisibleStatus;
            }
            else
            {
                _chatPageVisibleStatus = false;
                this.Size = new Size(this.Width - 300, this.Height);
                chatPageContainer.Visible = _chatPageVisibleStatus;
            }
        }

        private void btnRecBoardcast_Click(object sender, EventArgs e)
        {
            if (!_screenRecServerStatus)
            {
                _screenRecServerStatus = true;

                string url = string.Concat("http://", _remoteServerIPAddr, ":", _remoteScreenSharingPort);
                MJPEGStream stream = new MJPEGStream(url);
                videoPlayer.VideoSource = stream;
                videoPlayer.Start();
            }
            else
            {
                _screenRecServerStatus = false;
                videoPlayer.Stop();
            }
            
        }


        public delegate void ServerConfigChangedDelegate(string serverIPAddr, int screenShanringPort);
        public event ServerConfigChangedDelegate ServerConfigChangedEvent;
        /// <summary>
        /// 右上角设置按钮
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void MainForm_ExtendBoxClick(object sender, EventArgs e)
        {
            ServerConfig config = new ServerConfig();
            config.SetLocalServerParam(_localServerIPAddr, _localMessagePort, _localScreenSharingPort);
            config.SetRemoteServerParam(_remoteServerIPAddr, _remoteMessagePort, _remoteScreenSharingPort);

            FServerConfig fServerConfig = new FServerConfig(config);
            fServerConfig.Render();
            fServerConfig.ShowDialog();
            if (fServerConfig.IsOK)
            {
                if (_streamingServer != null && _streamingServer.IsRunning)
                {
                    Invoke(new Action(() =>
                    {
                        ShowWarningDialog("正在屏幕演示,无法修改配置参数!");
                    }));
                    return;
                }

                // 修改完成后的服务配置信息
                config = fServerConfig.ServerConfig;

                // TODO:// 这里需要添加是否修改的逻辑判断,
                // 但是有一点需要注意: 当服务第一次重启失败后, 我们需要进行修改参数,但是若只是判断两次值是否一致,那么将会导致服务无法启动
                _localServerIPAddr = config.LocalServerAddress;
                _localMessagePort = config.LocalMessagePort;
                _localScreenSharingPort = config.LocalScreenSharingPort;

                _remoteServerIPAddr = config.RemoteServerIPAddr;
                _remoteMessagePort = config.RemoteMessagePort;
                _remoteScreenSharingPort = config.RemoteScreenSharingPort;

                _udpServer.RestartServer(_localServerIPAddr, _localMessagePort);

                ServerConfigChangedEvent?.Invoke(_localServerIPAddr, _localMessagePort);
            }
            fServerConfig.Dispose();
        }

        private async void btnSendMsg_Click(object sender, EventArgs e)
        {
            if (!_udpServer.RunningStatus)
            {
                ShowWarningTip("服务还未启动,请修改参数重新启动哦~");
                return;
            }

            string strMessage = txtMessageInput.Text.ToString().Trim();

            txtMessageInput.Clear();
            txtMessageInput.Focus();
            if (strMessage.Length <= 0)
            {
                ShowWarningTip("不可以发送空消息哦!");
                return;
            }

            byte[] byteMsg = Encoding.UTF8.GetBytes(strMessage);
            Datagram datagram = new Datagram(Identity.Student, DataGramType.Message, byteMsg);

            datagram.RemoteIPAddr = IPAddress.Parse(_remoteServerIPAddr);
            datagram.RemotePort = _remoteMessagePort;

            await _udpServer.SendDataGram(datagram);
        }

        private void MainForm_Load(object sender, EventArgs e)
        {
            Console.WriteLine("   ,-,--.    _,.----.                ,---.   ,--.--------.  \r\n ,-.'-  _\\ .' .' -   \\  .-.,.---.  .--.'  \\ /==/,  -   , -\\ \r\n/==/_ ,_.'/==/  ,  ,-' /==/  `   \\ \\==\\-/\\ \\\\==\\.-.  - ,-./ \r\n\\==\\  \\   |==|-   |  .|==|-, .=., |/==/-|_\\ |`--`\\==\\- \\    \r\n \\==\\ -\\  |==|_   `-' \\==|   '='  /\\==\\,   - \\    \\==\\_ \\   \r\n _\\==\\ ,\\ |==|   _  , |==|- ,   .' /==/ -   ,|    |==|- |   \r\n/==/\\/ _ |\\==\\.       /==|_  . ,'./==/-  /\\ - \\   |==|, |   \r\n\\==\\ - , / `-.`.___.-'/==/  /\\ ,  )==\\ _.\\=\\.-'   /==/ -/   \r\n `--`---'             `--`-`--`--' `--`           `--`--`   ");

            try
            {
                _udpServer = UDPMessageServer.GetInstance();
                _udpServer.DataReceiveEvent += OnDataReceive;
                _udpServer.ServerStartSuccess += OnServerStartSuccess;
                _udpServer.ServerErrorEvent += OnServerError;
                _udpServer.DataSendError += OnDataSendError;
                _udpServer.DataSendSuccess += OnDataSendSuccess;

                _udpServer.Start(_localServerIPAddr, _localMessagePort);
            }
            catch (Exception ex)
            {
                Invoke(new Action(() =>
                {
                    UINotifier.Show("聊天室启动失败:" + ex.Message.ToString(),
                           UINotifierType.ERROR, "Error", false, 3000);
                }));
            }

            lbChatList.Items.Clear();
        }

        private void OnDataSendSuccess(Datagram datagram)
        {
            datagram.Identity = Identity.MySelf;

            switch (datagram.DataGramType)
            {
                case DataGramType.Message:
                    string[] messageByte = FormatDatagram.FormatMessage(datagram);
                    BeginInvoke(new Action(() =>
                    {
                        lbChatList.Items.Add(messageByte[0]);
                        lbChatList.Items.Add(messageByte[1]);
                        lbChatList.TopIndex = lbChatList.Count - 2;
                    }));
                    break;
                case DataGramType.OuterData:
                    string[] outerDataByte = FormatDatagram.FormatMessage(datagram);
                    BeginInvoke(new Action(() =>
                    {
                        lbChatList.Items.Add(outerDataByte[0]);
                        lbChatList.Items.Add(outerDataByte[1]);
                        lbChatList.TopIndex = lbChatList.Count - 2;
                    }));
                    break;
                case DataGramType.RaisHands:
                    // 显示到消息列表上
                    Invoke(new Action(() =>
                    {
                        UINotifier.Show("您整理思绪,在课堂上举手!",
                               UINotifierType.INFO, "举手!", false, 3000);
                    }));
                    break;
                case DataGramType.Online:
                    // do nothing!
                    break;
                case DataGramType.Downline:
                    // do nothing!
                    break;
                case DataGramType.FileInfo:
                    // 什么都不做或者显示到消息列表上
                    break;
                default:
                    Console.WriteLine("OnDataSendSuccess(Datagram datagram) default is executed!");
                    break;

            }
        }

        private void OnDataSendError(string errorMessage)
        {
            Invoke(new Action(() =>
            {
                UINotifier.Show(errorMessage,
                       UINotifierType.ERROR, "Error", false, 3000);
            }));
        }

        private void OnServerError(string errorMessage)
        {
            // 服务启动失败也需要发送一下下线消息
            SendDownlineSignal();

            Invoke(new Action(() =>
            {
                UINotifier.Show("聊天室启动失败:" + errorMessage,
                       UINotifierType.ERROR, "Error", false, 3000);
            }));
        }

        private void OnServerStartSuccess()
        {

            // 每10s发送上线消息
            timerSendOnlineSignal.Start();
            

            Invoke(new Action(() =>
            {
                UINotifier.Show("聊天室启动成功:" + _localServerIPAddr + ":" + _localMessagePort,
                       UINotifierType.INFO, "Success", false, 3000);
            }));
        }

        private void OnDataReceive(Datagram datagram)
        {
            switch (datagram.DataGramType)
            {
                case DataGramType.Message:
                    string[] messageByte = FormatDatagram.FormatMessage(datagram);
                    BeginInvoke(new Action(() =>
                    {
                        lbChatList.Items.Add(messageByte[0]);
                        lbChatList.Items.Add(messageByte[1]);
                        lbChatList.TopIndex = lbChatList.Count - 2;
                    }));
                    break;
                case DataGramType.OuterData:
                    string[] outerDataByte = FormatDatagram.FormatMessage(datagram);
                    BeginInvoke(new Action(() =>
                    {
                        lbChatList.Items.Add(outerDataByte[0]);
                        lbChatList.Items.Add(outerDataByte[1]);
                        lbChatList.TopIndex = lbChatList.Count - 2;
                    }));
                    break;
                case DataGramType.RaisHands:
                    // do noting!
                    break;
                case DataGramType.Online:
                    // do nothing!
                    break;
                case DataGramType.Downline:
                    // do nothing!
                    break;
                case DataGramType.RequireStuDemonstrate:
                    // 接收到请求演示, 发布
                    try
                    {
                        // 开始广播
                        ConfigureUiAtServerStartup();

                        _streamingServer = StreamingServer.GetInstance(Resolution.Resolutions.OneThousandAndEightyP,
                            Fps.OneHundredAndTwenty, true);
                        _streamingServer.Start(IPAddress.Parse(_localServerIPAddr), _localScreenSharingPort);

                        Invoke(new Action(() =>
                        { 
                            ShowSuccessNotifier("屏幕共享服务启动成功:" + _localServerIPAddr + ":" + _localScreenSharingPort); 
                        }));
                    }
                    catch (FormatException ex)
                    {
                        UIMessageDialog.ShowErrorDialog(this, ex.StackTrace.ToString());
                        ConfigureUiWhenServerStoped();
                    }
                    catch (SocketException ex)
                    {
                        UIMessageDialog.ShowErrorDialog(this, ex.StackTrace.ToString());
                        ConfigureUiWhenServerStoped();
                    }
                    break;
                case DataGramType.FileInfo:
                    // TODO:// 在这里接收文件...
                    YFileInfo info = (YFileInfo)datagram.Data;

                    Invoke(new Action( async() =>
                    {
                        if (ShowAskDialog("是否接收该文件:" + info.Filename, true))
                        {
                            using (Stream ntStream = await TCPFileStreamClient.GetInstance()
                                .ConnectedAndRecFileStream(datagram.RemoteIPAddr, 8888))
                            { 
                                SaveFileDialog saveFileDialog1 = new SaveFileDialog();
                                saveFileDialog1.Filter = "txt files (*.txt)|*.txt|All files (*.*)|*.*";
                                saveFileDialog1.FilterIndex = 2;
                                saveFileDialog1.RestoreDirectory = true;
                                saveFileDialog1.FileName = info.Filename;

                                if (saveFileDialog1.ShowDialog() == DialogResult.OK)
                                {
                                    try
                                    {
                                        string fullname = saveFileDialog1.FileName;
                                        using (var fStream = File.Create(fullname))
                                        {
                                            await ntStream.CopyToAsync(fStream);
                                        }

                                    }
                                    catch (IOException ex)
                                    {
                                    }
                                    finally
                                    {
                                        ntStream.Flush();
                                        ntStream.Close();
                                        ShowSuccessDialog("保存成功!");
                                    }
                                
                                }
                            }
                        }
                        
                    }));
                    break;
                default:
                    Console.WriteLine("OnDataReceive(Datagram datagram) default is executed!");
                    break;

            }
        }


        private void btnStartDemonstrate_Click(object sender, EventArgs e)
        {
            if (btnStartDemonstrate.Text == "停止演示" && _streamingServer.IsRunning)
            {
                _streamingServer?.Stop();
                ConfigureUiWhenServerStoped();
            }
        }

        private void ConfigureUiWhenServerStoped()
        {
            btnStartDemonstrate.Enabled = false;
            btnStartDemonstrate.Text = "演示状态";
            btnStartDemonstrate.Symbol = 61515;

            btnStartDemonstrate.FillColor = Color.FromArgb(80, 160, 255);
            btnStartDemonstrate.FillHoverColor = Color.FromArgb(115, 179, 255);
            btnStartDemonstrate.FillPressColor = Color.FromArgb(64, 128, 204);
        }

        private void ConfigureUiAtServerStartup()
        {
            btnStartDemonstrate.Enabled = true;
            btnStartDemonstrate.Text = "停止演示"; 
            btnStartDemonstrate.Symbol = 61517;

            btnStartDemonstrate.FillColor = Color.FromKnownColor(KnownColor.IndianRed);
            btnStartDemonstrate.FillHoverColor = Color.FromArgb(219, 111, 111);
            btnStartDemonstrate.FillPressColor = Color.FromArgb(180, 82, 82);
        }

        private void btnClearMessageContainer_Click(object sender, EventArgs e)
        {
            lbChatList?.Items.Clear();
        }

        private async void btnRaiseHands_Click(object sender, EventArgs e)
        {
            if (_udpServer != null && _udpServer.RunningStatus)
            {
                Datagram datagram = new Datagram(Identity.Student, DataGramType.RaisHands, null);
                datagram.RemoteIPAddr = IPAddress.Parse(_remoteServerIPAddr);
                datagram.RemotePort = _remoteMessagePort;

                await _udpServer.SendDataGram(datagram);
            }
        }

        private async void timerSendOnlineSignal_Tick(object sender, EventArgs e)
        {
            if (_udpServer != null && _udpServer.RunningStatus)
            {
                // 服务启动成功, 向教师机发送上线消息
                Datagram datagram = new Datagram(Identity.Student, DataGramType.Online, null);
                datagram.RemoteIPAddr = IPAddress.Parse(_remoteServerIPAddr);
                datagram.RemotePort = _remoteMessagePort;

                await _udpServer.SendDataGram(datagram);
            }
        }

        private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
        {

            SendDownlineSignal();
            _udpServer?.Stop();
            timerSendOnlineSignal?.Stop();
            _streamingServer?.Stop();
        }

        private async void SendDownlineSignal()
        {
            if (_udpServer != null && _udpServer.RunningStatus)
            {
                Datagram datagram = new Datagram(Identity.Student, DataGramType.Downline, null);
                datagram.RemoteIPAddr = IPAddress.Parse(_remoteServerIPAddr);
                datagram.RemotePort = _remoteMessagePort;

                await _udpServer.SendDataGram(datagram);
            }
        }

    }
}
